#include "types.h"
#include "stat.h"
#include "user.h"

int cowforkTest_globalVar;
char *cowforkTest_globalVar_function;

void cowforkTest()
{
  cowforkTest_globalVar=2;
  int pid = cowfork();
  if(pid==0)		// if child
	cowforkTest_globalVar=1;
  else
	wait();
  // printing should be: if pid 0 ,has a global variable of value 1, else ,has a global variable of value 2
  printf(1,"proccess with pid %d ,has a global variable of value %d\n",pid,cowforkTest_globalVar);
  /************** test that changing a variable will get the page back to its "writable" flag as was before the sharing *******************/
  // the writable flag on the text of cowforkTest should have been 0 before the sharing
  cowforkTest_globalVar_function = (char*)cowforkTest;
  //char *f = (char*)cowforkTest;
  //*f=123;
  //hence shouldnt be able to change value:
  *cowforkTest_globalVar_function = 123;
}

void testtask2()
{
  int* pointerToAddress;
  pointerToAddress = (int*)4096;
  int a = *pointerToAddress;
  printf(1,"Test read from second page - to check other pages read wasnt destroyed\n");
  
  pointerToAddress = (int*)0;
  int b = *pointerToAddress;
  printf(1,"Test Failed - succeeded reading from first page\n");
  while(1);		// inifinite loop - never stop the proccess
  *pointerToAddress = *pointerToAddress+b;
  *pointerToAddress = *pointerToAddress+a;
}


void f();

void f()
{
	printf(1,"f called\n");
}

void testtask3(void)
{
  printf(1,"calling f\n");
  f();
  char *F= (char*)&testtask3;
  F=F+0x15;
  printf(1,"MEM of f:%x\n",*F);
  printf(1,"calling f\n");
  f();
  int pid = fork();
  if(pid!=0)
	wait();
  printf(1,"writing to MEM of f\n");
  *F++=0x90;
  *F++=0x90;
  *F++=0x90;
  *F++=0x90;
  *F++=0x90;
  F=F-5;
  printf(1,"MEM of f:%x\n",*F);
  printf(1,"calling f\n");
  f();
  while(1);		// inifinite loop - never stop the proccess
}


int
main(int argc, char *argv[])
{
  int pid;
  //$$$$$$$$$$$$$$$$$$$$$$$$$$$$ TEST1.1 - check different pages in mem
  printf(1,"\n***************** TEST1.1 - DIFFERENT PAGES IN MEM ****************\n");
  int* t1;
  t1 = malloc(400);
  t1[0] = 1;
  int* t2;
  t2 = malloc(400);
  t2[0] = 1;
  int* t3;
  t3 = malloc(400);
  t3[0] = 1;
  t1[1] = t2[0]+t3[0];
  printf(1,"TEST FOR DIFFERENT PAGES IN MEM\n");
  sleep(500);		
  // time to check ^p
  // CORRECT OUTPUT - several pages in testsanity proccess (probably proccess 4)
  //$$$$$$$$$$$$$$$$$$$$$$$$$$$$ TEST1.2 - fork
  printf(1,"\n***************** TEST1.2 - FORK ****************\n");
  pid = fork();
  if(pid == 0)	// if child
  {
      printf(1,"TEST FORK\n");
      sleep(500);		
      // time to check ^p
      // CORRECT OUTPUT - same number of pages as the parent proccess, but:
      // different virtual address for these pages and different physical address for them,
      // then the addresses in the parent process. pay attention that the two proccesses has the same name
      exit();
      // not returning to here
  }
  else
  {
      sleep(500);
      wait();
  }
  
  //$$$$$$$$$$$$$$$$$$$$$$$$$$$$ TEST1.3 - forkcow
  printf(1,"\n***************** TEST1.3 - COWFORK ****************\n");
  pid = cowfork();
  // BASICLY, IF THE CHECK PID==0 WORKS AT THE CHILD, I THINK ITS MEANS ENOUGH THAT
  // THE COW SUCCEEDED TO COPY WHEN PID WAS WRITTEN TO
  if(pid == 0)	// if child
  {  
      printf(1,"test cowfork\n");
      sleep(1000);
      exit();
      // not returning to here
  }
  else
  {
      sleep(500);
      // TEST1.4 - COW copied correctly, once a variable chanced in the parent
      t1[0] = 2;
      printf(1,"test that COW copied correctly, once a variable chanced in the parent\n");
      sleep(500);
      wait();
  }
  
  //$$$$$$$$$$$$$$$$$$$$$$$$$$$$ TEST2 - task2
  printf(1,"\n***************** TEST2 - TASK2 (NULL POINTING TO PAGE 0) ****************\n");
  pid = fork();
  if(pid == 0)	// if child
  {
      testtask2();
      // not returning to here
      exit();
  }
  else
  {
      wait();
      printf(1,"Q2 TEST SUCCEEDED - child proccess failed to write to first page, hence finished on error\n\n");
  }
  //$$$$$$$$$$$$$$$$$$$$$$$$$$$$ TEST3 - task3
  printf(1,"\n***************** TEST3 - TASK3 (READ_ONLY WRITTEN) ****************\n");
  pid = fork();
  if(pid == 0)	// if child
  {
      testtask3();
      // not returning to here
      exit();
  }
  else
  {
      wait();
      printf(1,"Q3 TEST SUCCEEDED - child proccess failed to write to text segment, hence finished on error\n\n");
  }
  
  //$$$$$$$$$$$$$$$$$$$$$$$$$$$$ TEST4 - forkcow for several proccesses - check counting and sharing
  printf(1,"\n***************** TEST4 - PAGE SHARING OF SEVERAL PROCCESSES ****************\n");
  pid = cowfork();
  // BASICLY, IF THE CHECK PID==0 WORKS AT THE CHILD, I THINK ITS MEANS ENOUGH THAT
  // THE COW SUCCEEDED TO COPY WHEN PID WAS WRITTEN TO
  if(pid == 0)	// if first child
  {  
      sleep(2500);
      t1[0] = 1;
      printf(1,"TEST SEVERAL PROCESSES SHARING - test that COW copied correctly at first child, parent remain same page frame \n");
      exit();
      // not returning to here
  }
  else
  {
      int pid2 = cowfork();
      if(pid2 == 0)	// if second child
      {
	 printf(1,"TEST SEVERAL PROCESSES SHARING - test that at first its only referances in both children\n");
	 sleep(500);
	  // TEST4.2 - COW copied correctly at second child, but first child remain a referance
	  t1[0] = 2;
	  printf(1,"TEST SEVERAL PROCESSES SHARING - test that COW copied correctly at second child, first child remained a referance\n");
	  sleep(1000);
	  exit();
	  // not returning to here
      }
      else
      {
	wait();		// wait for first child
	wait();		// wait for Second child
	t1[0] = 0;
	sleep(500);
      }
  }
  
  printf(1,"\n***************** TEST5 - ANOTHER TEST FOR COW ****************\n");
  cowforkTest();			// another test for cowfork
  
  exit();
}
